﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SpeakupBackend.Themes;
using SpeakupBackend.Classes;
using TableParsers;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;

namespace SpeakupTests.SpeakupBackend.Themes.Workshop
{
    [TestClass]
    public class WorkshopThemeTests
    {
        [TestMethod]
        public void CreateWorkshopTheme()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();

            foreach (WorkshopClassType ct in WorkshopClassType.GetWorkshopClassTypes())
            {
                WorkshopTheme wsth = new WorkshopTheme(ct, levelGroup, week, "some thName");
                Assert.AreEqual(ct, wsth.ClassType);
            }

            foreach (LevelGroup lg in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                WorkshopTheme wsth = new WorkshopTheme(classType, lg, week, "some thName");
                Assert.AreEqual(lg, wsth.LevelGroup);
            }

            foreach (Week w in Week.GetWeeks())
            {
                WorkshopTheme wsth = new WorkshopTheme(classType, levelGroup, w, "some thName");
                Assert.AreEqual(w, wsth.Week);
            }

            Random rnd = new Random();
            for (int i = 0; i < 10; i++)
            {
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < 10; j++)
                {
                    sb.Append(rnd.Next('a', 'z'));
                }

                WorkshopTheme wsth = new WorkshopTheme(classType, levelGroup, week, sb.ToString());
                Assert.AreEqual(sb.ToString(), wsth.Name);
            }
        }

        class Counter
        {
            public int count = 0;
        }

        [TestMethod]
        public void WorkshopThemeSetName()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            WorkshopTheme theme = new WorkshopTheme(classType, levelGroup, week, name); 
            Counter counter = new Counter();

            theme.PropertyChanged += (s, e) =>
            {
                Assert.AreEqual("Name", e.PropertyName);
                counter.count++;
            };


            counter.count = 0;
            theme.Name = name;
            Assert.AreEqual(0, counter.count);

            counter.count = 0;
            theme.Name = name+"some postfix";
            Assert.AreEqual(1, counter.count);
        }

        [TestMethod]
        public void WorkshopThemeObjectEquals()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            foreach (WorkshopClassType ctLeft in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (WorkshopClassType ctRight in WorkshopClassType.GetWorkshopClassTypes())
                {
                    Object left = new WorkshopTheme(ctLeft, levelGroup, week, name);
                    Object right = new WorkshopTheme(ctRight, levelGroup, week, name);

                    Assert.AreEqual(ctLeft == ctRight, left.Equals(right));
                    Assert.AreEqual(ctLeft == ctRight, right.Equals(left));
                }
            }

            foreach (LevelGroup lgLeft in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                foreach (LevelGroup lgRight in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    Object left = new WorkshopTheme(classType, lgLeft, week, name);
                    Object right = new WorkshopTheme(classType, lgRight, week, name);

                    Assert.AreEqual(lgLeft == lgRight, left.Equals(right));
                    Assert.AreEqual(lgLeft == lgRight, right.Equals(left));
                }
            }

            foreach (Week wkLeft in Week.GetWeeks())
            {
                foreach (Week wkRight in Week.GetWeeks())
                {
                    Object left = new WorkshopTheme(classType, levelGroup, wkLeft, name);
                    Object right = new WorkshopTheme(classType, levelGroup, wkRight, name);

                    Assert.AreEqual(wkLeft == wkRight, left.Equals(right));
                    Assert.AreEqual(wkLeft == wkRight, right.Equals(left));
                }
            }

            //workshop theme should ignore thName during comparison
            List<String> names = new List<String> { "name1", "name2", "name3" };
            foreach (String nLeft in names)
            {
                foreach (String rLeft in names)
                {
                    Object left = new WorkshopTheme(classType, levelGroup, week, nLeft);
                    Object right = new WorkshopTheme(classType, levelGroup, week, rLeft);

                    Assert.AreEqual(true, left.Equals(right));
                    Assert.AreEqual(true, right.Equals(left));
                }
            }

            //null reference equality
            {
                Object left = new WorkshopTheme(classType, levelGroup, week, name);
                Object right = null;

                Assert.IsFalse(left.Equals(right));
            }

            //invalid object comaparison
            {
                Object left = new WorkshopTheme(classType, levelGroup, week, name);
                Object right = (int)3;

                Assert.IsFalse(left.Equals(right));
            }

        }

        [TestMethod]
        public void WorkshopThemeIEquatableEquals()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            foreach (WorkshopClassType ctLeft in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (WorkshopClassType ctRight in WorkshopClassType.GetWorkshopClassTypes())
                {
                    WorkshopTheme left = new WorkshopTheme(ctLeft, levelGroup, week, name);
                    WorkshopTheme right = new WorkshopTheme(ctRight, levelGroup, week, name);

                    Assert.AreEqual(ctLeft == ctRight, left.Equals(right));
                    Assert.AreEqual(ctLeft == ctRight, right.Equals(left));
                }
            }

            foreach (LevelGroup lgLeft in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                foreach (LevelGroup lgRight in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, lgLeft, week, name);
                    WorkshopTheme right = new WorkshopTheme(classType, lgRight, week, name);

                    Assert.AreEqual(lgLeft == lgRight, left.Equals(right));
                    Assert.AreEqual(lgLeft == lgRight, right.Equals(left));
                }
            }

            foreach (Week wkLeft in Week.GetWeeks())
            {
                foreach (Week wkRight in Week.GetWeeks())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, wkLeft, name);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, wkRight, name);

                    Assert.AreEqual(wkLeft == wkRight, left.Equals(right));
                    Assert.AreEqual(wkLeft == wkRight, right.Equals(left));
                }
            }

            //workshop theme should ignore thName during comparison
            List<String> names = new List<String> { "name1", "name2", "name3" };
            foreach (String nLeft in names)
            {
                foreach (String rLeft in names)
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, week, nLeft);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, week, rLeft);

                    Assert.AreEqual(true, left.Equals(right));
                    Assert.AreEqual(true, right.Equals(left));
                }
            }

            //null reference equality
            {
                WorkshopTheme left = new WorkshopTheme(classType, levelGroup, week, name);
                WorkshopTheme right = null;

                Assert.IsFalse(left.Equals(right));
            }
        }

        [TestMethod]
        public void WorkshopThemeGetHashCode()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            foreach (WorkshopClassType ctLeft in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (WorkshopClassType ctRight in WorkshopClassType.GetWorkshopClassTypes())
                {
                    WorkshopTheme left = new WorkshopTheme(ctLeft, levelGroup, week, name);
                    WorkshopTheme right = new WorkshopTheme(ctRight, levelGroup, week, name);

                    Assert.AreEqual(ctLeft == ctRight, left.GetHashCode() == right.GetHashCode());                    
                }
            }

            foreach (LevelGroup lgLeft in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                foreach (LevelGroup lgRight in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, lgLeft, week, name);
                    WorkshopTheme right = new WorkshopTheme(classType, lgRight, week, name);

                    Assert.AreEqual(lgLeft == lgRight, left.GetHashCode() == right.GetHashCode());                    
                }
            }

            foreach (Week wkLeft in Week.GetWeeks())
            {
                foreach (Week wkRight in Week.GetWeeks())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, wkLeft, name);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, wkRight, name);

                    Assert.AreEqual(wkLeft == wkRight, left.GetHashCode() == right.GetHashCode());                        
                }
            }

            //workshop theme should ignore thName during comparison
            List<String> names = new List<String> { "name1", "name2", "name3" };
            foreach (String nLeft in names)
            {
                foreach (String rLeft in names)
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, week, nLeft);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, week, rLeft);

                    Assert.IsTrue(left.GetHashCode() == right.GetHashCode());                    
                }
            }
        }

        [TestMethod]
        public void WorkshopThemeCompareTo()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            foreach (WorkshopClassType ctLeft in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (WorkshopClassType ctRight in WorkshopClassType.GetWorkshopClassTypes())
                {
                    WorkshopTheme left = new WorkshopTheme(ctLeft, levelGroup, week, name);
                    WorkshopTheme right = new WorkshopTheme(ctRight, levelGroup, week, name);

                    Assert.AreEqual(Math.Sign(ctLeft.CompareTo(ctRight)),
                                    Math.Sign(left.CompareTo(right)));
                }
            }

            foreach (LevelGroup lgLeft in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                foreach (LevelGroup lgRight in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, lgLeft, week, name);
                    WorkshopTheme right = new WorkshopTheme(classType, lgRight, week, name);

                    Assert.AreEqual(Math.Sign(lgLeft.CompareTo(lgRight)),
                                    Math.Sign(left.CompareTo(right)) );
                }
            }

            foreach (Week wkLeft in Week.GetWeeks())
            {
                foreach (Week wkRight in Week.GetWeeks())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, wkLeft, name);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, wkRight, name);

                    Assert.AreEqual(Math.Sign(wkLeft.CompareTo(wkRight)),
                                    Math.Sign(left.CompareTo(right)));
                }
            }

            //workshop theme should ignore thName during comparison
            List<String> names = new List<String> { "name1", "name2", "name3" };
            foreach (String nLeft in names)
            {
                foreach (String rLeft in names)
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, week, nLeft);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, week, rLeft);

                    Assert.AreEqual(0, Math.Sign(left.CompareTo(right)));
                }
            }
        }

        [TestMethod]
        public void WorkshopThemeComparisonOperators()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            foreach (WorkshopClassType ctLeft in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (WorkshopClassType ctRight in WorkshopClassType.GetWorkshopClassTypes())
                {
                    WorkshopTheme left = new WorkshopTheme(ctLeft, levelGroup, week, name);
                    WorkshopTheme right = new WorkshopTheme(ctRight, levelGroup, week, name);

                    Assert.AreEqual(ctLeft == ctRight, left == right);
                    Assert.AreEqual(ctLeft != ctRight, left != right);
                    Assert.AreEqual(ctLeft <  ctRight, left <  right);
                    Assert.AreEqual(ctLeft <= ctRight, left <= right);
                    Assert.AreEqual(ctLeft >  ctRight, left >  right);
                    Assert.AreEqual(ctLeft >= ctRight, left >= right);
                }
            }

            foreach (LevelGroup lgLeft in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                foreach (LevelGroup lgRight in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, lgLeft, week, name);
                    WorkshopTheme right = new WorkshopTheme(classType, lgRight, week, name);

                    Assert.AreEqual(lgLeft == lgRight, left == right);
                    Assert.AreEqual(lgLeft != lgRight, left != right);
                    Assert.AreEqual(lgLeft < lgRight, left < right);
                    Assert.AreEqual(lgLeft <= lgRight, left <= right);
                    Assert.AreEqual(lgLeft > lgRight, left > right);
                    Assert.AreEqual(lgLeft >= lgRight, left >= right);
                }
            }

            foreach (Week wkLeft in Week.GetWeeks())
            {
                foreach (Week wkRight in Week.GetWeeks())
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, wkLeft, name);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, wkRight, name);

                    Assert.AreEqual(wkLeft == wkRight, left == right);
                    Assert.AreEqual(wkLeft != wkRight, left != right);
                    Assert.AreEqual(wkLeft < wkRight, left < right);
                    Assert.AreEqual(wkLeft <= wkRight, left <= right);
                    Assert.AreEqual(wkLeft > wkRight, left > right);
                    Assert.AreEqual(wkLeft >= wkRight, left >= right);
                }
            }

            //workshop theme should ignore thName during comparison
            List<String> names = new List<String> { "name1", "name2", "name3" };
            foreach (String nLeft in names)
            {
                foreach (String rLeft in names)
                {
                    WorkshopTheme left = new WorkshopTheme(classType, levelGroup, week, nLeft);
                    WorkshopTheme right = new WorkshopTheme(classType, levelGroup, week, rLeft);

                    Assert.IsTrue(left == right);
                    Assert.IsFalse(left != right);
                    Assert.IsFalse( left < right);
                    Assert.IsTrue( left <= right);
                    Assert.IsFalse( left > right);
                    Assert.IsTrue(left >= right);
                }
            }
        }

        Class CreateClass(WorkshopClassType classType, LevelGroup levelGroup, Week week)
        {
            return CreateClass(classType.ClassType,
                levelGroup,
                week);
        }

        Class CreateClass(ClassType classType, LevelGroup levelGroup, Week week)
        {
            return CreateClass(classType,
                Level.GetLevel(levelGroup.MinLevel).GetUnits().First(),
                week);
        }

        Class CreateClass(ClassType classType, int unit, Week week)
        {
            DateTime name = DateTime.Now;
            while (Week.GetWeek(name) != week)
            {
                name += TimeSpan.FromDays(7);
            }

            Class cl = new Class(0,
                new ClassName("fullName", "thName", classType),
                new ClassTime(name, TimeSpan.FromHours(12), TimeSpan.FromHours(13)),
                unit,
                ClassStatus.Available,
                ClassAction.None);

            return cl;
        }
        
        [TestMethod]
        public void WorkshopThemeIsAssignableClass()
        {
            WorkshopClassType classType = WorkshopClassType.GetWorkshopClassTypes().First();
            LevelGroup levelGroup = LevelGroup.DefaultFactory.GetLevelGroups().First();
            Week week = Week.GetWeeks().First();
            String name = "some thName";

            foreach (WorkshopClassType themeCt in WorkshopClassType.GetWorkshopClassTypes())
            {
                WorkshopTheme theme = new WorkshopTheme(themeCt, levelGroup, week, name);
                foreach (ClassType classCt in Enum.GetValues(typeof(ClassType)))
                {
                    Class cl = CreateClass(classCt, levelGroup, week);

                    Assert.AreEqual(classCt == themeCt, theme.IsCompatibleClass(cl));
                }
            }

            foreach (LevelGroup themeLg in LevelGroup.DefaultFactory.GetLevelGroups())
            {
                WorkshopTheme theme = new WorkshopTheme(classType, themeLg, week, name);
                for (int i = Level.MinValidUnitNumber; i <= Level.MinValidUnitNumber; i++ )
                {
                    Class cl = CreateClass(classType, i, week);

                    Assert.AreEqual(themeLg.ContainsLevel(Level.GetLevelByUnit(i)),
                                    theme.IsCompatibleClass(cl));
                }

                Class noUnitCl = CreateClass(classType, 0, week);
                Assert.IsFalse(theme.IsCompatibleClass(noUnitCl));
            }

            foreach (Week themeWk in Week.GetWeeks())
            {
                WorkshopTheme theme = new WorkshopTheme(classType, levelGroup, themeWk, name);
                foreach (Week classWk in Week.GetWeeks())
                {
                    Class cl = CreateClass(classType, levelGroup, classWk);

                    Assert.AreEqual(classWk == themeWk, theme.IsCompatibleClass(cl));
                }

            }
        }

        [TestMethod]
        public void WorkshopThemeBinarySerilization()
        {
            List<String> names = new List<string> { "name1", "name2", "name3" };

            foreach (WorkshopClassType ct in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (LevelGroup lg in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    foreach (Week wk in Week.GetWeeks())
                    {
                        foreach (String name in names)
                        {
                            MemoryStream stream = new MemoryStream();
                            BinaryFormatter formatter = new BinaryFormatter();

                            WorkshopTheme theme = new WorkshopTheme(ct, lg, wk, name);

                            formatter.Serialize(stream, theme);
                            stream.Seek(0, SeekOrigin.Begin);
                            theme = (WorkshopTheme)formatter.Deserialize(stream);

                            Assert.AreEqual(ct, theme.ClassType);
                            Assert.AreEqual(lg, theme.LevelGroup);
                            Assert.AreEqual(wk, theme.Week);
                            Assert.AreEqual(name, theme.Name);
                        }
                    }
                }
            }
        }

        [TestMethod]
        public void WorkshopThemeXmlSerilization()
        {
            List<String> names = new List<string> { "name1", "name2", "name3" };

            foreach (WorkshopClassType ct in WorkshopClassType.GetWorkshopClassTypes())
            {
                foreach (LevelGroup lg in LevelGroup.DefaultFactory.GetLevelGroups())
                {
                    foreach (Week wk in Week.GetWeeks())
                    {
                        foreach(String name in names)
                        {
                            MemoryStream stream = new MemoryStream();
                            XmlSerializer serializer = new XmlSerializer(typeof(WorkshopTheme));

                            WorkshopTheme theme = new WorkshopTheme(ct, lg, wk, name);

                            serializer.Serialize(stream, theme);
                            stream.Seek(0, SeekOrigin.Begin);
                            theme = (WorkshopTheme)serializer.Deserialize(stream);

                            Assert.AreEqual(ct, theme.ClassType);
                            Assert.AreEqual(lg, theme.LevelGroup);
                            Assert.AreEqual(wk, theme.Week);
                            Assert.AreEqual(name, theme.Name);
                        }
                    }
                }
            }
        }

    }
}
